#include "adc_module.h"
#include "log_module.h"
#include "alarm_module.h"
#include <rtdevice.h>

static adc_config_t *adc_config = RT_NULL;
static rt_thread_t adc_thread = RT_NULL;
static rt_adc_device_t adc_dev = RT_NULL;

/* Filter buffers */
static struct {
    adc_filter_type_t type;
    uint8_t window_size;
    uint8_t index;
    float buffer[20];
    float sum;
} adc_filters[ADC_CHANNELS];

static float moving_average_filter(uint8_t channel, float new_value)
{
    if (adc_filters[channel].window_size == 0) {
        return new_value;
    }
    
    /* Subtract the oldest value */
    adc_filters[channel].sum -= adc_filters[channel].buffer[adc_filters[channel].index];
    
    /* Add the new value */
    adc_filters[channel].buffer[adc_filters[channel].index] = new_value;
    adc_filters[channel].sum += new_value;
    
    /* Update index */
    adc_filters[channel].index = (adc_filters[channel].index + 1) % adc_filters[channel].window_size;
    
    /* Return average */
    return adc_filters[channel].sum / adc_filters[channel].window_size;
}

static float exponential_filter(uint8_t channel, float new_value)
{
    /* Simple exponential filter: y(n) = α * x(n) + (1-α) * y(n-1) */
    float alpha = 0.3f; /* Adjustable factor */
    
    if (adc_filters[channel].window_size == 0) {
        adc_filters[channel].buffer[0] = new_value;
        return new_value;
    }
    
    adc_filters[channel].buffer[0] = alpha * new_value + (1 - alpha) * adc_filters[channel].buffer[0];
    return adc_filters[channel].buffer[0];
}

static int float_compare(const void *a, const void *b)
{
    float fa = *(const float*)a;
    float fb = *(const float*)b;
    return (fa > fb) - (fa < fb);
}

static float median_filter(uint8_t channel, float new_value)
{
    /* Add new value to buffer */
    adc_filters[channel].buffer[adc_filters[channel].index] = new_value;
    adc_filters[channel].index = (adc_filters[channel].index + 1) % adc_filters[channel].window_size;
    
    /* Create temporary buffer for sorting */
    float temp_buffer[20];
    rt_memcpy(temp_buffer, adc_filters[channel].buffer, sizeof(float) * adc_filters[channel].window_size);
    
    /* Sort and return median */
    qsort(temp_buffer, adc_filters[channel].window_size, sizeof(float), float_compare);
    return temp_buffer[adc_filters[channel].window_size / 2];
}

static float apply_filter(uint8_t channel, float raw_value)
{
    switch (adc_filters[channel].type) {
        case FILTER_MOVING_AVERAGE:
            return moving_average_filter(channel, raw_value);
        case FILTER_EXPONENTIAL:
            return exponential_filter(channel, raw_value);
        case FILTER_MEDIAN:
            return median_filter(channel, raw_value);
        case FILTER_NONE:
        default:
            return raw_value;
    }
}

static float adc_raw_to_current(uint16_t raw_value)
{
    /* Assuming 12-bit ADC, 0-3.3V range */
    float voltage = (raw_value / 4095.0f) * 3.3f;
    
    /* Assuming 250Ω resistor for 4-20mA conversion */
    /* Voltage across resistor: V = I * R = 0.02A * 250Ω = 5V max */
    /* But our ADC only goes to 3.3V, so we need voltage divider or different resistor */
    
    /* For 165Ω resistor: 20mA * 165Ω = 3.3V exactly */
    float current_ma = (voltage / 165.0f) * 1000.0f;
    
    return current_ma;
}

static float current_to_engineering(uint8_t channel, float current_ma)
{
    float engineering_value;
    
    /* Linear mapping from current to engineering value */
    if (current_ma <= adc_config->channels[channel].min_current) {
        engineering_value = adc_config->channels[channel].min_engineering;
    } else if (current_ma >= adc_config->channels[channel].max_current) {
        engineering_value = adc_config->channels[channel].max_engineering;
    } else {
        float ratio = (current_ma - adc_config->channels[channel].min_current) / 
                     (adc_config->channels[channel].max_current - 
                      adc_config->channels[channel].min_current);
        engineering_value = adc_config->channels[channel].min_engineering + 
                           ratio * (adc_config->channels[channel].max_engineering - 
                                   adc_config->channels[channel].min_engineering);
    }
    
    /* Apply linear calibration */
    engineering_value = linear_calibration(engineering_value, 
                                          adc_config->base.k_factor, 
                                          adc_config->base.b_offset);
    
    return engineering_value;
}

static rt_err_t adc_hardware_init(void)
{
    /* Find ADC device */
    adc_dev = (rt_adc_device_t)rt_device_find("adc1");
    if (adc_dev == RT_NULL) {
        LOG_E("ADC device not found\n");
        return RT_ERR_NOT_FOUND;
    }
    
    /* Enable ADC channels */
    for (int i = 0; i < ADC_CHANNELS; i++) {
        rt_adc_enable(adc_dev, i);
    }
    
    return RT_OK;
}

static void process_adc_message(message_t *msg)
{
    if (msg == RT_NULL) return;
    
    switch (msg->msg_type) {
        case MSG_CONTROL_CMD:
            if (msg->data != RT_NULL && msg->data_len == sizeof(adc_calibration_t)) {
                adc_calibration_t *cal = (adc_calibration_t *)msg->data;
                adc_set_calibration(cal->channel, cal->min_current, cal->max_current,
                                   cal->min_engineering, cal->max_engineering);
            }
            break;
            
        default:
            break;
    }
}

rt_err_t adc_module_init(void)
{
    rt_err_t result;
    
    /* Get configuration pointer */
    adc_config = &g_system_config.adc;
    
    /* Initialize hardware */
    result = adc_hardware_init();
    if (result != RT_OK) {
        LOG_E("ADC hardware initialization failed\n");
        return result;
    }
    
    /* Initialize filters */
    for (int i = 0; i < ADC_CHANNELS; i++) {
        adc_filters[i].type = FILTER_MOVING_AVERAGE;
        adc_filters[i].window_size = 10;
        adc_filters[i].index = 0;
        adc_filters[i].sum = 0;
        rt_memset(adc_filters[i].buffer, 0, sizeof(adc_filters[i].buffer));
    }
    
    /* Create ADC thread */
    adc_thread = rt_thread_create("adc", 
                                 adc_thread_entry, 
                                 RT_NULL, 
                                 1024, 
                                 11, 
                                 20);
    if (adc_thread == RT_NULL) {
        LOG_E("Failed to create ADC thread\n");
        return RT_ERR_MEM;
    }
    
    LOG_I("ADC module initialized successfully\n");
    return RT_OK;
}

void adc_thread_entry(void *parameter)
{
    rt_uint32_t tick;
    uint16_t raw_value;
    float current_ma, engineering_value;
    
    while (1) {
        tick = rt_tick_get();
        
        /* Read all ADC channels */
        for (int i = 0; i < ADC_CHANNELS; i++) {
            if (!adc_config->channels[i].enabled) {
                continue;
            }
            
            /* Read raw ADC value */
            raw_value = rt_adc_read(adc_dev, i);
            
            /* Convert to current */
            current_ma = adc_raw_to_current(raw_value);
            
            /* Apply filter */
            current_ma = apply_filter(i, current_ma);
            
            /* Convert to engineering value */
            engineering_value = current_to_engineering(i, current_ma);
            
            /* Update configuration */
            adc_config->channels[i].adc_raw = raw_value;
            adc_config->channels[i].current_value = current_ma;
            adc_config->channels[i].engineering_value = engineering_value;
        }
        
        /* Send data ready message */
        send_message(MODULE_ALARM, MSG_DATA_UPDATE, RT_NULL, 0);
        send_message(MODULE_MODBUS, MSG_DATA_UPDATE, RT_NULL, 0);
        
        /* Wait for next cycle */
        rt_thread_delay_until(&tick, rt_tick_from_millisecond(adc_config->base.update_interval));
    }
}

float adc_get_engineering_value(uint8_t channel)
{
    if (channel >= ADC_CHANNELS || adc_config == RT_NULL) {
        return 0.0f;
    }
    
    return adc_config->channels[channel].engineering_value;
}

rt_err_t adc_set_calibration(uint8_t channel, float min_current, float max_current, 
                            float min_engineering, float max_engineering)
{
    if (channel >= ADC_CHANNELS || adc_config == RT_NULL) {
        return RT_ERR_PARAM;
    }
    
    adc_config->channels[channel].min_current = min_current;
    adc_config->channels[channel].max_current = max_current;
    adc_config->channels[channel].min_engineering = min_engineering;
    adc_config->channels[channel].max_engineering = max_engineering;
    
    LOG_I("ADC channel %d calibration updated: %.1f-%.1fmA -> %.1f-%.1f\n", 
          channel, min_current, max_current, min_engineering, max_engineering);
    
    return RT_OK;
}

rt_err_t adc_set_filter_type(uint8_t channel, adc_filter_type_t type, uint8_t window_size)
{
    if (channel >= ADC_CHANNELS) {
        return RT_ERR_PARAM;
    }
    
    if (window_size > 20) {
        window_size = 20;
    }
    
    adc_filters[channel].type = type;
    adc_filters[channel].window_size = window_size;
    adc_filters[channel].index = 0;
    adc_filters[channel].sum = 0;
    rt_memset(adc_filters[channel].buffer, 0, sizeof(float) * 20);
    
    return RT_OK;
}

adc_config_t *adc_get_config(void)
{
    return adc_config;
}